假设,DOM 被设置了 height:20px 和 overflow:hidden,如何计算它的真实高度呢?

一、问题背景

最近在优化折叠组件,需要在窗口宽度变化的时候重新判断展开收起状态。如果段落高度大于给定高度,则隐藏超高内容,展示【展开】按钮。如果段落高度小于给定高度,则不限制最大高度,隐藏【展开】按钮。

但如何计算【段落高度小于给定高度】呢?比如,段落被设置了 height="20px",如何计算它的真实高度呢?

二、干货,计算元素真实高度函数

方法如下,直接传入要计算的目标 dom 就好了。

function getHeightUnfold (dom) {
    var fakeNode = dom.cloneNode(true);
    fakeNode.style.position = 'absolute';
    // 先插入再改样式,以防元素属性在createdCallback中被添加覆盖
    dom.parentNode.insertBefore(fakeNode, dom);
    fakeNode.style.height = 'auto';
    fakeNode.style.visibility = 'hidden';

    var height = fakeNode.getBoundingClientRect().height;
    dom.parentNode.removeChild(fakeNode);

    return height;
}

这个方法的核心是,创建一个不可见元素,摘除高度限制,最终计算它的高度。

三、发散思考

1. 复制元素的必要性

Jenny_L 给复制出来的元素增加了 postiion: absolute 属性,为了不触发后面元素的重拍重绘,节省浏览器资源。如果直接快速地给目标元素设置 height: auto + 获取高度 + height: 20px,虽然能达到目的,但会造成所有后续元素的(不一定可见)抖动,尽量避免。

2. Node.cloneNode 与 document.createElement('div') 的选择

后者与innerHTML配合使用,虽然能够模仿目标元素的内层内容,但不能继承目标元素的样式。即使使用document.createElement(dom.nodeName)也会有问题,不能继承内联样式。而使用cloneNode不但可以继承 class,css,还能触发 createdCallback(如果有的话),继承 js 中添加的内联样式。

3. fakeNode.getBoundingClientRect().height 与 getComputedStyle(fakeNode).height 的选择

都是计算高度的,但前者计算的是占位高度,包括 padding+border;后者计算的是单纯高度,经过多层 css 优先级竞争之后的 height 取值(px),获取纯数值还需要parseInt()。本次情况,需要计算占位高度,所以选择getBoundingClientRect()

4. removeChild 的必要性

虽然 fakeNode 不可见,但终究在文档流中,display 不是 none,重拍的时候会参与计算。除此之外,如果原先 dom 带有 id="someID" 的话,删除 fakeNode 之前,文档中就会存在两个 id="someID" 的元素。未来浏览器再做选择的时候,就懵逼了。

四、自勉

好久不写文章了,草稿箱里存了好多代码片段,要加油了。

不得不说,这种小代码片段还是很有分享价值的,一次研究(竟然花了一个小时),未来处处复制,走向人生巅峰。


梁小米jenny
1 声望0 粉丝